﻿using System.Collections.Generic;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.Rendering;

#if UNITY_EDITOR
using UnityEditor;
#endif

namespace Mars.GpuInstance
{
    public class GPUAnimatorInstancerProceduralManager : MonoBehaviour
    {
        public int maxDrawDis = 500;

        [Header("资源缓存的时间")] public int assetCacheTime = 900;
        

        private Bounds m_DrawBounds = new Bounds();

        private float4[] m_FrustumPlanesArray = new float4[6];
        private NativeArray<float4> m_FrustumPlanes;

        private Vector3 perPlayerPos = Vector3.zero;
        private Quaternion perPlayerRot = Quaternion.identity;
        
        private Camera m_MainCamera;

        private Dictionary<int2, GPUAnimator> animatorDict = new();

        JobHandle jobHandle = default;

        public Dictionary<int, GPUAnimatorInstancerRuntimeData> instancerRuntimeDatas = new ();

        private float _lastTransitionUpdateTime;
        private static float _transitionFrequency = 1.0f / 60.0f;

        private bool isInit = false;

        private bool drawShadow = true;
        private bool shieldShadow = false;

        private List<int> needRemoveList = new List<int>();

        private bool anyModelChange = false; //是否有模型变化，有变化就要刷新texture streaming

        public bool isEnable = true;

        private static GPUAnimatorInstancerProceduralManager instance;

        public static GPUAnimatorInstancerProceduralManager GetInstance()
        {
#if UNITY_EDITOR
            if (instance == null)
#else
            if (instance is null)
#endif
            {
                instance = GameObject.FindObjectOfType<GPUAnimatorInstancerProceduralManager>();
                if (instance == null)
                {
                    var go = new GameObject("GPUAnimatorInstancerProceduralManager");
                    instance = go.AddComponent<GPUAnimatorInstancerProceduralManager>();
                    DontDestroyOnLoad(go);
                    instance.Init();
                }
            }

            return instance;
        }

        public void Init(Camera camera, int drawDis = 2800)
        {
            maxDrawDis = drawDis;
            m_MainCamera = camera;
            m_DrawBounds.size = Vector3.one * 2000;
            m_FrustumPlanes = new NativeArray<float4>(6, Allocator.Persistent);

            isInit = true;
        }

        public void Init(int drawDis = 2800)
        {
            maxDrawDis = drawDis;
            m_DrawBounds.size = Vector3.one * 2000;
            m_FrustumPlanes = new NativeArray<float4>(6, Allocator.Persistent);

            isInit = true;
        }

        public void UpdateDrawCamera(Camera camera)
        {
            m_MainCamera = camera;
        }

        public void UpdateDrawDis(int drawDis)
        {
            maxDrawDis = drawDis;
        }

        public void UpdateEnableShadow(bool enableShadow)
        {
            drawShadow = enableShadow;
        }

        public void ShieldShadow(bool shield)
        {
            shieldShadow = shield;
        }
        
        public void Dispose()
        {
            if (m_FrustumPlanes.IsCreated)
            {
                m_FrustumPlanes.Dispose();
            }

            Clear();
        }

        public void Clear()
        {
            var instancerRuntimeDataValues = instancerRuntimeDatas.Values;
            foreach (var instancerRuntimeData in instancerRuntimeDataValues)
            {
                instancerRuntimeData.Dispose();
            }

            instancerRuntimeDatas.Clear();
            animatorDict.Clear();
            GPUAnimatorInstanceCacheDataManager.Instance.ClearGPUSkinData();
        }

        protected void Start()
        {
            if (!isInit)
            {
                Init();
            }
        }

        protected void Update()
        {
            if (!isEnable)
            {
                return;
            }

            if (m_MainCamera == null)
            {
                m_MainCamera = Camera.main;
            }

#if UNITY_EDITOR
            else
            {
                if (SceneView.lastActiveSceneView && m_MainCamera == SceneView.lastActiveSceneView.camera)
                {
                    m_MainCamera = Camera.main;
                }
            }
            if (m_MainCamera == null)
            {
                m_MainCamera = SceneView.lastActiveSceneView.camera;
            }
#endif

            UpdateFrame();

            var frameCount = Time.frameCount;
            var cacheMgr = GPUAnimatorInstanceCacheDataManager.Instance;
            foreach (var instancerRuntimeData in instancerRuntimeDatas)
            {
                if (instancerRuntimeData.Value.input.Length <= 0 &&
                    frameCount - instancerRuntimeData.Value.clearTimeFrame > assetCacheTime) //没有使用的资源60秒后清除掉
                {
                    cacheMgr.RecycleGPUAnimatorInstancerRuntimeData(instancerRuntimeData.Value);
                    needRemoveList.Add(instancerRuntimeData.Key);
                }
            }

            foreach (var key in needRemoveList)
            {
                instancerRuntimeDatas.Remove(key);
            }

            needRemoveList.Clear();

            GPUAnimatorInstanceCacheDataManager.Instance.Tick();
        }

        protected void LateUpdate()
        {
            if (isEnable)
            {
                Draw();
            }
        }

        protected void OnDestroy()
        {
            Dispose();
        }

        protected void OnApplicationQuit()
        {
            GPUAnimatorInstanceCacheDataManager.Instance.ClearGPUSkinData();
        }

        public void Draw()
        {
            Shader.SetGlobalFloat(InstancerConstants.CURRENT_TIME, Time.time);
            var instancerRuntimeDataValues = instancerRuntimeDatas.Values;
            var shadow = !shieldShadow && drawShadow;
            foreach (var instancerRuntimeData in instancerRuntimeDataValues)
            {
                instancerRuntimeData.Draw(m_DrawBounds, shadow);
            }
        }

        public void UpdateFrame()
        {
            if (m_MainCamera == null)
            {
                return;
            }

            _transitionFrequency = 1.0f / 60.0f * Time.timeScale;

            float currentTime = Time.time;
            bool calculateTransitions = currentTime - _lastTransitionUpdateTime > _transitionFrequency;

            foreach (var instancerRuntimeData in instancerRuntimeDatas.Values)
            {
                if (calculateTransitions)
                {
                    int transitionCount = instancerRuntimeData.transitioningAnimators.Count;
                    if (transitionCount > 0)
                    {
                        for (int i = 0; i < transitionCount; i++)
                        {
                            if (!instancerRuntimeData.transitioningAnimators[i]
                                .ApplyTransition(instancerRuntimeData, currentTime))
                            {
                                i--;
                                transitionCount--;
                            }
                        }

                        instancerRuntimeData.animationDataModified = true;
                    }
                }
            }

            if (calculateTransitions)
            {
                _lastTransitionUpdateTime = currentTime;
            }

            var updateType = IsNeedUpdateData();
            if (updateType != InstancerDataDirtyType.NONE)
            {
                if (!jobHandle.IsCompleted)
                {
                    jobHandle.Complete();
                }

                m_FrustumPlanes.CopyFrom(MathUtil.GetFrustumPlanes(m_MainCamera, m_FrustumPlanesArray));
                var instancerRuntimeDataValues = instancerRuntimeDatas.Values;

                var jobHandleList = new NativeList<JobHandle>(instancerRuntimeDataValues.Count * 2, Allocator.Temp);

                Profiler.BeginSample("Add Job");
                foreach (var instancerRuntimeData in instancerRuntimeDataValues)
                {
                    if (!instancerRuntimeData.dataModified && updateType == InstancerDataDirtyType.INSTANCER_CHANGE)
                    {
                        continue;
                    }

                    instancerRuntimeData.VisibilityCalculation(m_FrustumPlanes, perPlayerPos, jobHandleList);
                }

                Profiler.EndSample();

                Profiler.BeginSample("Complete Visibility Job");
                jobHandle = JobHandle.CombineDependencies(jobHandleList.AsArray());
                jobHandle.Complete();
                Profiler.EndSample();
            }

            var copyDataJobHandleList = new NativeList<JobHandle>(instancerRuntimeDatas.Count * 2, Allocator.Temp);
            foreach (var items in instancerRuntimeDatas)
            {
                if (items.Value.animationDataModified || items.Value.animatorDataModified)
                {
                    items.Value.CopyAnimationData(copyDataJobHandleList);
                }
            }

            Profiler.BeginSample("Complete Copy Animation Job");
            jobHandle = JobHandle.CombineDependencies(copyDataJobHandleList.AsArray());
            jobHandle.Complete();
            Profiler.EndSample();


            var aniJobHandleList = new NativeList<JobHandle>(instancerRuntimeDatas.Count * 2, Allocator.Temp);
            foreach (var items in instancerRuntimeDatas)
            {
                UpdateAnimatorsDataByJob(items.Value, aniJobHandleList);
            }

            Profiler.BeginSample("Complete Animation Job");
            jobHandle = JobHandle.CombineDependencies(aniJobHandleList.AsArray());
            jobHandle.Complete();
            Profiler.EndSample();

            Profiler.BeginSample("SubmitToGPU");
            foreach (var instancerRuntimeData in instancerRuntimeDatas)
            {
                instancerRuntimeData.Value.SubmitToGPU();
            }

            Profiler.EndSample();

            m_DrawBounds.center = perPlayerPos;
        }

        public void UpdateAnimatorsDataByJob(GPUAnimatorInstancerRuntimeData runtimeData, NativeList<JobHandle> handles)
        {
            runtimeData.CalculateAnim(handles);
        }

        public void InitRuntimeData(GPUAnimatorGenConfig prefab)
        {
#if (UNITY_EDITOR || UNITY_STANDALONE) && !DEVELOPMENT_BUILD
            bool hasError = false;
            if (prefab.animationData == null)
            {
                hasError = true;
            }
            else
            {
                if (prefab.animationData.clipDataList.Count == 0)
                {
                    hasError = true;
                }

                if (prefab.animationData.animTex == null)
                {
                    hasError = true;
                }
            }

            foreach (var lodData in prefab.LodDatas)
            {
                foreach (var renderData in lodData.renderDatas)
                {
                    if (renderData.sharedMesh == null)
                    {
                        hasError = true;
                        break;
                    }
                }
            }

            if (prefab.prefabName == 0)
            {
                hasError = true;
            }

            if (hasError)
            {
                return;
            }
#endif

            var prefabName = prefab.prefabName;
            if (instancerRuntimeDatas.ContainsKey(prefabName))
            {
                return;
            }

            var cacheMgr = GPUAnimatorInstanceCacheDataManager.Instance;

            Profiler.BeginSample("GPUAnimatorInstancerRuntimeData InitRuntimeData");
            var runtimeData = cacheMgr.GetOrCreateGPUAnimatorInstancerRuntimeData();

            var mpb = new MaterialPropertyBlock();
            runtimeData.UpdateMpb(mpb);
            runtimeData.UpdateTotalBoneCount(prefab.animationData.totalBoneCount);
            runtimeData.UpdateFrameRate(prefab.frameRate);
            runtimeData.UpdateAnimationTex(prefab.animationData.animTex);
            runtimeData.UpdateClipBoneData(prefab.animationData, prefab.name);
            runtimeData.frameRate = prefab.frameRate;
            runtimeData.textureSizeX = prefab.animationData.textureSizeX;

            if (runtimeData.animationClipDataDict == null)
            {
                runtimeData.animationClipDataDict = new Dictionary<int, GPUAnimationClipData>();
            }

            if (runtimeData.clipHashToIndex == null)
            {
                runtimeData.clipHashToIndex = new Dictionary<int, int>();
            }

            for (int k = 0; k < prefab.animationData.clipDataList.Count; k++)
            {
                var hashCode = Animator.StringToHash(prefab.animationData.clipDataList[k].clipName);
                runtimeData.animationClipDataDict.Add(hashCode, prefab.animationData.clipDataList[k]);
                runtimeData.clipHashToIndex.Add(hashCode, k);
            }

            for (int i = 0; i < prefab.animationData.bones.Count; i++)
            {
                runtimeData.AddAnchorNameToBoneIndex(
                    Animator.StringToHash(prefab.animationData.bones[i].boneTransformName),
                    prefab.animationData.bones[i].boneIndex);
            }

            Profiler.BeginSample("GPUAnimatorInstancerRuntimeData InitRuntimeData AnchorInfo");
            if (prefab.anchorInfo)
            {
                Profiler.BeginSample("GPUAnimatorInstancerRuntimeData InitRuntimeData loadData");
                var slice = prefab.anchorInfo.GetData<AnchorInfo>();
                Profiler.EndSample();
                runtimeData.UpdateAnchorInfos(slice);
            }

            Profiler.EndSample();


            bool reachImpostorShowRange = false;
            int drawDis = 0;
            int lodIndex = 0;

            // 添加正常渲染 batch

            for (int i = 0; i < prefab.LodDatas.Count; i++)
            {
                var lodData = prefab.LodDatas[i];

                var lodDis = math.min(maxDrawDis, lodData.lodDis);
                var drawRange = new float2(drawDis, lodDis);
                drawDis = math.min(maxDrawDis, lodData.lodDis);

                for (int j = 0; j < lodData.renderDatas.Count; j++)
                {
                    var renderData = lodData.renderDatas[j];

                    if (renderData.sharedMesh == null || renderData.sharedMaterial == null)
                    {
                        continue;
                    }

                    var sharedMaterial = renderData.sharedMaterial;
                    GPURenderBatch batch = null;
                    GPUSkinnedMeshData smd = null;
                    Mesh shareMesh = renderData.sharedMesh;

                    if (renderData.isSkinMesh)
                    {
                        smd = prefab.animationData.GetSkinnedMeshDataByName(renderData.name);
                        batch = cacheMgr.GetOrCreateGPUSkinRenderBatch();
                        batch.Init(shareMesh, sharedMaterial, mpb, drawRange, prefab.boxExtents);
                        if (smd.hasBindPoseOffset)
                        {
                            batch.UpdateBoneOffset(smd.bindPoseOffset);
                        }

                        sharedMaterial.EnableKeyword("_USE_INSTANCING_ANIM");
                    }
                    else
                    {
                        batch = cacheMgr.GetOrCreateGPUMeshRenderBatch();
                        batch.Init(shareMesh, sharedMaterial, mpb, drawRange, prefab.boxExtents);
                    }

                    batch.UpdateTransformOffset(renderData.transformOffset);
                    batch.lodLevel = lodIndex++;

                    if (i == prefab.LodDatas.Count - 1 && prefab.animatorShadowLod != ShadowLod.FOLLOW_LOD)
                    {
                        if (renderData.shadowCastingMode != ShadowCastingMode.Off)
                        {
                            GPURenderBatch shadowBatch = null;
                            if (renderData.isSkinMesh)
                            {
                                shadowBatch = cacheMgr.GetOrCreateGPUSkinRenderBatch();
                                shadowBatch.Init(shareMesh, sharedMaterial, mpb, new float2(0, drawRange.y), prefab.boxExtents);

                                if (smd.hasBindPoseOffset)
                                {
                                    shadowBatch.UpdateBoneOffset(smd.bindPoseOffset);
                                }

                                sharedMaterial.EnableKeyword("_USE_INSTANCING_ANIM");
                            }
                            else
                            {
                                shadowBatch = cacheMgr.GetOrCreateGPUMeshRenderBatch();
                                shadowBatch.Init(shareMesh, sharedMaterial, mpb, new float2(0, drawRange.y), prefab.boxExtents);
                            }

                            shadowBatch.UpdateTransformOffset(renderData.transformOffset);
                            runtimeData.AddShadowBatch(shadowBatch);

                            if (renderData.isSkinMesh)
                            {
                                // 当播放到存在非等比缩放的 clip 时，要开启一个 keyword，因此单独分成一个 batch
                                var nonPropScalingBatch = cacheMgr.GetOrCreateGPUSkinRenderBatch();
                                nonPropScalingBatch.Init(shadowBatch, true);
                                runtimeData.AddShadowBatch(nonPropScalingBatch);
                            }
                        }
                    }

                    runtimeData.animatorShadowLod = prefab.animatorShadowLod;
                    runtimeData.AddGPUSkinRenderBatch(batch);

                    if (renderData.isSkinMesh)
                    {
                        // 当播放到存在非等比缩放的 clip 时，要开启一个 keyword，因此单独分成一个 batch
                        var nonPropScalingBatch = cacheMgr.GetOrCreateGPUSkinRenderBatch();
                        nonPropScalingBatch.Init(batch, true);
                        runtimeData.AddGPUSkinRenderBatch(nonPropScalingBatch);
                    }
                }
            }

            instancerRuntimeDatas.Add(prefabName, runtimeData);
            Profiler.EndSample();
        }

        public void AddInstance(GPUAnimatorGenConfig prefab, int id, Vector3 pos, Vector3 scale, Quaternion rot)
        {
            InitRuntimeData(prefab);
            if (instancerRuntimeDatas.TryGetValue(prefab.prefabName, out var item))
            {
                if (item.AddInstance(id, pos, scale, rot))
                {
                    animatorDict.TryAdd(new int2(prefab.prefabName, id),
                        GPUAnimatorInstanceCacheDataManager.Instance.GetOrCreateGPUAnimator());
                }

                anyModelChange = true;
            }
        }

        public void AddInstance(GPUAnimatorGenConfig prefab, int id, Vector3 pos, Vector3 scale, Quaternion rot,
            float4x4 offset)
        {
            InitRuntimeData(prefab);
            var matrix = float4x4.TRS(pos, rot, scale);
            matrix = math.mul(matrix, offset);
            AddInstance(prefab, id, matrix);
        }

        public void AddInstance(GPUAnimatorGenConfig prefab, int id, float4x4 matrix)
        {
            var prefabName = prefab.prefabName;

            InitRuntimeData(prefab);
            if (instancerRuntimeDatas.TryGetValue(prefabName, out var item))
            {
                if (item.AddInstance(id, matrix))
                {
                    animatorDict.TryAdd(new int2(prefabName, id),
                        GPUAnimatorInstanceCacheDataManager.Instance.GetOrCreateGPUAnimator());
                }

                anyModelChange = true;
            }
        }

        public void ReplaceInstance(GPUAnimatorGenConfig oldPrefab, GPUAnimatorGenConfig prefab, int id,
            float4x4 matrix)
        {
            ReplaceInstance(oldPrefab.prefabName, prefab, id, matrix);
        }

        public void ReplaceInstance(int oldPrefabName, GPUAnimatorGenConfig prefab, int id, float4x4 matrix)
        {
            if (!instancerRuntimeDatas.TryGetValue(oldPrefabName, out var oldItem))
            {
                AddInstance(prefab, id, matrix);
            }
            else
            {
                oldItem.RemoveInstance(id);
                var hasOldAnimator = animatorDict.Remove(new int2(oldPrefabName, id), out var oldAnimator);

                InitRuntimeData(prefab);
                var prefabName = prefab.prefabName;
                if (instancerRuntimeDatas.TryGetValue(prefabName, out var item))
                {
                    if (item.AddInstance(id, matrix))
                    {
                        animatorDict.TryAdd(new int2(prefabName, id),
                            hasOldAnimator
                                ? oldAnimator
                                : GPUAnimatorInstanceCacheDataManager.Instance.GetOrCreateGPUAnimator());
                        if (hasOldAnimator) // 重新应用动画数据到新的runtimeData
                        {
                            var index = item.GetInstanceIndex(id);
                            oldAnimator.ApplyToRuntimeData(item, index);
                        }
                    }
                }
            }
        }

        public void ClearInstance(GPUAnimatorGenConfig prefab)
        {
            if (prefab)
            {
                ClearInstance(prefab.prefabName);
            }
        }

        public void ClearInstance(int prefabName)
        {
            if (instancerRuntimeDatas.TryGetValue(prefabName, out var item))
            {
                if (item.idToIndexMap.IsCreated)
                {
                    foreach (var kv in item.idToIndexMap)
                    {
                        var hasAnimator = animatorDict.Remove(new int2(prefabName, kv.Key), out var animator);
                        if (hasAnimator)
                        {
                            GPUAnimatorInstanceCacheDataManager.Instance.RecycleGPUAnimator(animator);
                        }
                    }
                }

                item.ClearInstance();
                anyModelChange = true;
            }
        }

        public void RemoveInstance(GPUAnimatorGenConfig prefab, int id)
        {
            if (prefab == null)
            {
                return;
            }

            RemoveInstance(prefab.prefabName, id);
        }

        public void RemoveInstance(int prefabName, int id)
        {
            if (instancerRuntimeDatas.TryGetValue(prefabName, out var item))
            {
                item.RemoveInstance(id);
                var key = new int2(prefabName, id);
                //移除动画控制器
                if (animatorDict.Remove(key, out var animator))
                {
                    GPUAnimatorInstanceCacheDataManager.Instance.RecycleGPUAnimator(animator);
                }

                anyModelChange = true;
            }
        }
        
        public void PlayAnimatorByIndex(GPUAnimatorGenConfig prefab, int id, int clipIndex)
        {
            if (animatorDict.TryGetValue(new int2(prefab.prefabName, id), out var animator) &&
                instancerRuntimeDatas.TryGetValue(prefab.prefabName, out var runtimeData))
            {
                var clipData = prefab.animationData.clipDataList[clipIndex];
                var index = runtimeData.GetInstanceIndex(id);
                animator.StartAnimation(runtimeData, index, clipData);
            }
        }

        //播放动画
        public void PlayAnimator(GPUAnimatorGenConfig prefab, int id, int animHash, float normalizedTime, float speed,
            int fallbackHash = 0)
        {
            var prefabName = prefab.prefabName;
            if (animatorDict.TryGetValue(new int2(prefabName, id), out var animator) &&
                instancerRuntimeDatas.TryGetValue(prefabName, out var runtimeData))
            {
                if (!runtimeData.clipHashToIndex.TryGetValue(animHash, out var clipIndex))
                {
                    if (fallbackHash == 0 || !runtimeData.clipHashToIndex.TryGetValue(fallbackHash, out clipIndex))
                        return;
                }

                var clipData = prefab.animationData.clipDataList[clipIndex];
                var index = runtimeData.GetInstanceIndex(id);
                animator.StartAnimation(runtimeData, index, clipData, normalizedTime * clipData.length, speed);
            }
        }

        public void SetAnimationSpeed(GPUAnimatorGenConfig prefab, int id, float speed)
        {
            SetAnimationSpeed(prefab.prefabName, id, speed);
        }

        public void SetAnimationSpeed(int prefabName, int id, float speed)
        {
            if (animatorDict.TryGetValue(new int2(prefabName, id), out var animator) &&
                instancerRuntimeDatas.TryGetValue(prefabName, out var runtimeData))
            {
                var index = runtimeData.GetInstanceIndex(id);
                animator.SetAnimationSpeed(runtimeData, index, speed);
            }
        }

        public void CrossFadeAnimator(GPUAnimatorGenConfig prefab, int id, int animHash, float normalizedTime = 0,
            float normalizedTransitionTime = 0.2f)
        {
            var prefabName = prefab.prefabName;
            if (animatorDict.TryGetValue(new int2(prefabName, id), out var animator) &&
                instancerRuntimeDatas.TryGetValue(prefabName, out var runtimeData))
            {
                if (!runtimeData.clipHashToIndex.TryGetValue(animHash, out var clipIndex))
                {
                    return;
                }

                var clipData = prefab.animationData.clipDataList[clipIndex];
                var index = runtimeData.GetInstanceIndex(id);
                animator.StartCrossFade(runtimeData, index, clipData, normalizedTime * clipData.length,
                    normalizedTransitionTime * clipData.length);
            }
        }

        /// <summary>
        /// 获取挂点信息
        /// </summary>
        /// <param name="prefab">预设</param>
        /// <param name="id">id</param>
        /// <param name="anchorHash">用Animator.StringToHash转换挂点名称hash</param>
        /// <returns></returns>
        public AnchorInfo GetAnchorInfo(GPUAnimatorGenConfig prefab, int id, int anchorHash)
        {
            var prefabName = prefab.prefabName;
            if (instancerRuntimeDatas.TryGetValue(prefabName, out var runtimeData))
            {
                var index = runtimeData.GetInstanceIndex(id);
                return runtimeData.GetAnchorInfoByAnchorNameHash(anchorHash, index, prefab.frameRate);
            }

            return default;
        }

        public InstancerDataDirtyType IsNeedUpdateData()
        {
            if (IsCameraChange())
            {
                return InstancerDataDirtyType.CAMERA_CHANGE;
            }
            else
            {
                var instancerRuntimeDataValues = instancerRuntimeDatas.Values;
                foreach (var instancerRuntimeData in instancerRuntimeDataValues)
                {
                    if (instancerRuntimeData.dataModified)
                    {
                        return InstancerDataDirtyType.INSTANCER_CHANGE;
                    }
                }
            }

            return InstancerDataDirtyType.NONE;
        }

        public bool IsCameraChange()
        {
            if (m_MainCamera == null)
            {
                return false;
            }

            var pos = m_MainCamera.transform.position;
            var rot = m_MainCamera.transform.rotation;
            if (perPlayerPos != pos || perPlayerRot != rot)
            {
                perPlayerPos = pos;
                perPlayerRot = rot;
                return true;
            }

            return false;
        }

        public void SetDrawable(int pid, bool drawable)
        {
            if (instancerRuntimeDatas.TryGetValue(pid, out var item))
            {
                item.drawable = drawable;
            }
        }

        public bool GetDrawable(int pid)
        {
            if (instancerRuntimeDatas.TryGetValue(pid, out var item))
            {
                return item.drawable;
            }

            return false;
        }
    }
}